
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;

import static java.lang.System.out;

import aufgabe2.*;
import java.util.ArrayList;

/**
 * performs test cases to confirm the correct implementation
 * 
 * @author Lukas Koell
 * @author David Medenjak
 * @author Johannes Riedmann
 */
public class Test {

    /** horizontal ruler */
    static String hr = "-------------------------------------";
    static int dummy_count = 1;

    /**
     * @param args
     *            the command line arguments
     */
    public static void main(String[] args) throws Exception {
        AccessManager.getInstance().setUser(
                new Student(1234567, "Zack Brannigan"));
        out.println("Note that the ending '0' of course details is the maxSeat count...Have fun reading :)");
        out.println(hr);
        testCanCreateCourse();
        out.println(hr);
        testStudentListing();
        out.println(hr);
        testCanListCourses();
        out.println(hr);
        testCanSubscribeStudentToCourse();
        out.println(hr);
        testCanSubscribeStudentToExams();
        out.println(hr);
        testNoDoubleSubscriptionsPossible();
        out.println(hr);
        testSubscriptionOnlyPossibleDuringValidDates();
        out.println(hr);
        testStudentCanUnsubscribe();
        out.println(hr);
        testStudentCanOnlyUnsubscribeDuringValidPeriod();
        out.println(hr);
        testPersistentStore();
        out.println(hr);
        testAccessControl();
        out.println(hr);
        testCanEditCourseDetails();
        out.println(hr);
        testAddTeacherWithNotifications();
        out.println(hr);
        testDropCourseNotification();
        out.println(hr);
        testCanRestoreDroppedCourses();
        out.println(hr);
        testUserNeedsACoursePassedForSubscription();

        out.println(hr);
        testSubscriptionLimit();

        out.println(hr);
        out.println("Everything fine. Give us an A, please :D");
        // TODO: neue testcases hinzuf�gen
        // TODO: testcases f�r student notifications wenn sich am kurs was
        // �ndert
    }

    /**
     * every course has a maximum of seats students can only subscribe until
     * that limit
     */
    private static void testSubscriptionLimit() {
        out.println("A course with maxSeats = 1 should only allow 1 student to subscribe:");
        Course c1 = createDummyCourse();
        try {
            c1.setMaxSeats(1);
        } catch (CourseException e) {
            e.printStackTrace();
        } catch (AccessDeniedException e) {
            e.printStackTrace();
        }
        Student s1 = createDummyStudent();
        Student s2 = createDummyStudent();

        try {
            c1.subscribeStudent(s1, new GregorianCalendar(2000,
                    Calendar.JANUARY, 4));
            out.println("Passed: subscribing 1 student worked");
            c1.subscribeStudent(s2, new GregorianCalendar(2000,
                    Calendar.JANUARY, 4));
            out.println("Failed: subscribing the second worked too.");
        } catch (CourseException e) {
            out.println("Passed: Subscribing the second student did throw a CourseException!");
        }

    }

    /**
     * tests if a dropped course can be restored
     */
    private static void testCanRestoreDroppedCourses() {
        System.out.println("This is the current course list:");
        try {
            out.println(UniManager.getInstance().listCourses());
        } catch (AccessDeniedException e) {
            e.printStackTrace();
        }
        System.out.println("These are the dropped courses to show that we can restore them:");
        out.println(UniManager.getInstance().listDroppedCourses());
    }

    /**
     * Test if a new course can be created
     */
    private static void testCanCreateCourse() {
        out.println("Trying to create a course, this should be the stringified version:");
        out.println(createDummyCourse().toString());
    }

    /**
     * Test if we can list all courses
     * 
     * @throws AccessDeniedException
     */
    private static void testCanListCourses() throws AccessDeniedException {
        UniManager cm = UniManager.getInstance();
        Course dummy = createDummyCourse();
        try {
            out.println("Add a course and then...");
            cm.addCourse(dummy);
            out.println("...add the same course again...");
            cm.addCourse(dummy);
        } catch (CourseException e) {
            out.println(e.toString());
        }

        out.println("This is what the course list looks like: ");
        out.println(cm.listCourses());
    }

    /**
     * Test if a student can be added to a course
     * 
     * @throws CourseException
     */
    private static void testCanSubscribeStudentToCourse()
            throws CourseException {
        Course oop = createDummyCourse();
        Student zack = new Student(1234567, "Zack Brannigan");
        oop.subscribeStudent(zack, new GregorianCalendar(2000,
                Calendar.JANUARY, 4));

        out.println("We can subscribe a student to a course:");
        if (oop.numberOfSubscribedStudents() == 1) {
            System.out.println("Passed: subscription worked");
        } else {
            System.out.println("Failed: subscription did NOT work");
        }
    }

    /**
     * Test that no student can subscribe twice
     */
    private static boolean testNoDoubleSubscriptionsPossible() {
        Course oop = createDummyCourse();
        Student zack = new Student(1234567, "Zack Brannigan");
        // test passes if the CourseException is thrown
        try {
            oop.subscribeStudent(zack, new GregorianCalendar(2000,
                    Calendar.JANUARY, 5));
            oop.subscribeStudent(zack, new GregorianCalendar(2000,
                    Calendar.JANUARY, 6));
            out.println("Failed: Student subscribed twice");
            return false;
        } catch (CourseException e) {
            out.println(e);
            out.println("Passed: Tried to subscribe student twice to the same course.");
            return true;
        }
    }

    /**
     * Test if student can only subscribe within possible dates
     */
    private static boolean testSubscriptionOnlyPossibleDuringValidDates() {
        Course oop = createDummyCourse();
        Student zack = new Student(1234567, "Zack Brannigan");

        out.println("Testing to subscribe out of date (too early / too late):");
        // test passes when exception is thrown
        try {
            // too early
            oop.subscribeStudent(zack, new GregorianCalendar(1999,
                    Calendar.JANUARY, 20));
            out.println("Failed: Could subscribe too early");
        } catch (CourseException e) {
            out.println(e);
        }
        try {
            // too late
            oop.subscribeStudent(zack, new GregorianCalendar(2001,
                    Calendar.JANUARY, 5));
            out.println("Failed: Could subscribe too late");
            return false;
        } catch (CourseException e) {
            out.println(e);
        }
        out.println("Passed: Could not subscribe out of date.");
        return true;
    }

    /**
     * Test if Student can unsubscribe
     */
    private static boolean testStudentCanUnsubscribe() {
        out.println("Testing if student can unsubscribe...");

        Course oop = createDummyCourse();
        Student zack = new Student(1234567, "Zack Brannigan");

        try {
            oop.subscribeStudent(zack, new GregorianCalendar(2000,
                    Calendar.JANUARY, 5));
            oop.unsubscribeStudent(zack, new GregorianCalendar(2000,
                    Calendar.JANUARY, 20));

            if (oop.numberOfSubscribedStudents() == 0) {
                out.println("Passed: Student unsubscribed again.");
                return true;
            }
            out.println("Failed: Student didn't unsubscribe.");
            return false;
        } catch (CourseException e) {
            out.println("Failed: " + e);
            return false;
        }
    }

    /**
     * Test if Student can only unsubscribe during valid period
     */
    private static boolean testStudentCanOnlyUnsubscribeDuringValidPeriod() {
        Course oop = createDummyCourse();
        Student zack = new Student(1234567, "Zack Brannigan");
        out.println("Testing if Student can only unsubscribe during valid period...");
        try {
            oop.subscribeStudent(zack, new GregorianCalendar(2000,
                    Calendar.JANUARY, 5));
            oop.unsubscribeStudent(zack, new GregorianCalendar(2000,
                    Calendar.JANUARY, 27));
            out.println("Failed: Student unsubscribed after unsubscription end.");
            return false;
        } catch (CourseException e) {
            if (oop.numberOfSubscribedStudents() == 1) {
                out.println("Passed: Student still subscribed (" + e + ")");
                return true;
            }
            out.println("Failed: Student not subscribed any longer but exception thrown: "
                    + e);
            return false;
        }
    }

    /**
     * Test if listing of all course students works
     */
    private static void testStudentListing() throws CourseException {
        out.println("Testing listing of subscribed students");
        Course myCourse = new Course(1234, "OOP", new GregorianCalendar(2000,
                Calendar.JANUARY, 1), new GregorianCalendar(2000,
                Calendar.JANUARY, 10), new GregorianCalendar(2000,
                Calendar.JANUARY, 25), null, 0);

        Student testStudent = new Student(1234566, "Lukas Koell");
        Student testStudent2 = new Student(123444, "Zack Brannigan");

        myCourse.subscribeStudent(testStudent, new GregorianCalendar(2000,
                Calendar.JANUARY, 5));
        myCourse.subscribeStudent(testStudent2, new GregorianCalendar(2000,
                Calendar.JANUARY, 5));

        out.println("Does the student list work?");
        out.println(myCourse.listStudents());
    }

    /**
     * creates a dummy-Course-object, which is used to perform testcases
     */
    private static Course createDummyCourse() {
        return new Course(dummy_count++, "OOP", new GregorianCalendar(2000,
                Calendar.JANUARY, 1), new GregorianCalendar(2000,
                Calendar.JANUARY, 10), new GregorianCalendar(2000,
                Calendar.JANUARY, 25), null, 0);
    }

    /**
     * writes the data to file and reads it out again
     */
    private static boolean testPersistentStore() {
        AccessManager.getInstance().setUser(new Admin(1234));
        UniManager cm = UniManager.getInstance();
        try {
            Course c1 = createDummyCourse();
            Course c2 = createDummyCourse();
            cm.addCourse(c1);
            cm.addCourse(c2);

            c1.subscribeStudent(createDummyStudent(), new GregorianCalendar(
                    2000, Calendar.JANUARY, 4));

            Teacher t1 = new Teacher(351612);
            c1.addTeacher(t1);
            System.out.println("We now check if the persistent store works");
            out.println("This is the courselist before saving:");
            out.println(cm.listCourses());
            out.println();

        } catch (Exception e) {
            e.getStackTrace();
        }
        cm.serialize("coursemanager.data");
        System.out.println("Saving data was successful");

        System.out.println("After loading the data from disk, course list looks "
                + "like this (should be the same as above):");
        cm.unserialize("coursemanager.data");
        try {
            out.println(cm.listCourses());
        } catch (AccessDeniedException e) {
            e.printStackTrace();
        }

        return true;
    }

    /**
     * tests if access control works
     * 
     * @throws CourseException
     */
    private static void testDropCourseNotification() throws CourseException {

        Student zack = createDummyStudent();
        Course dummyCourse = createDummyCourse();
        dummyCourse.subscribeStudent(zack, new GregorianCalendar(2000,
                Calendar.JANUARY, 5));

        UniManager um = UniManager.getInstance();

        try {
            um.addCourse(dummyCourse);
        } catch (CourseException e) {
            System.out.println("Error on adding Course in Test "
                    + "testDropCourseNotification");
        }

        int before = um.getNumberOfCourses();
        um.dropCourse(dummyCourse);
        int after = um.getNumberOfCourses();

        System.out.println("Number of Courses before deleting: " + before
                + ", after: " + after + " (should be one less)");
        System.out.println(hr);
        System.out.println(zack.getName()
                + " received the following notifications during this test: ");
        System.out.println(zack.getNotifications());
    }

    /**
     * tests if access control works
     */
    private static void testAccessControl() {
        AccessManager am = AccessManager.getInstance();
        Student testStudent = new Student(1234566, "Zack Brannigan");
        am.setUser(testStudent);
        Course c = createDummyCourse();

        // System.out.println(hr);
        System.out.println("Student must not add teachers to a course "
                + "(This should throw a AccessDeniedException):");
        try {
            c.addTeacher(new Teacher(1234529));
        } catch (AccessDeniedException e) {
            System.out.println("Passed: " + e);
        }
    }

    /*
     * Test if notifications are received if a teacher is added.
     */
    private static void testAddTeacherWithNotifications()
            throws AccessDeniedException {
        Teacher t = new Teacher(1234567);
        t.setName("Michael Faad");

        Course c = createDummyCourse();

        // only admins can add teachers to courses
        Admin a = new Admin(123592);
        a.setName("Florian Silbereisen");
        AccessManager.getInstance().setUser(a);
        c.addTeacher(t);
        System.out.println("Admin " + a + " was able to add teacher " + t
                + " to a course");

        System.out.println(hr);

        Teacher t2 = new Teacher(1234444);
        t2.setName("Peter Purgathofer");
        c.addTeacher(t2);

        System.out.println("These are the notifications that the teachers "
                + "of the course receive on adding a teacher:");
        System.out.println("notifications for " + t + " (first added)");
        System.out.println(t.getNotifications());
        System.out.println("notifications for " + t2 + " (second added)");
        System.out.println(t2.getNotifications());
        System.out.println(hr);
        System.out.println("There should be 2 teachers on this course now:");
        System.out.println(c.getTeachers());
    }

    private static Student createDummyStudent() {
        return new Student(1024456, "Zack Brannigan");
    }

    private static void testCanEditCourseDetails() {
        out.println("Testing if one (first student, then teacher) can edit course settings...");
        AccessManager am = AccessManager.getInstance();
        Teacher testTeacher = new Teacher(2214);
        Student zack = createDummyStudent();
        Course c = createDummyCourse();

        am.setUser(zack);
        System.out.println("Before: " + c);
        try {
            try {
                c.setMaxSeats(999);
            } catch (CourseException e) {
                out.println(e);
            }
            c.setTitle("Not OOP");
            c.setUnsubscribeEnd(new GregorianCalendar(2013, 7, 15));
            System.out.println("Failed: " + c);
        } catch (AccessDeniedException e) {
            System.out.println("Passed: " + e);
        }

        am.setUser(testTeacher);
        System.out.println("Before: " + c);
        try {
            try {
                c.setMaxSeats(999);
            } catch (CourseException e) {
                out.println(e);
            }
            c.setTitle("Not OOP");
            c.setUnsubscribeEnd(new GregorianCalendar(2013, 7, 15));
            System.out.println("Passed: " + c);
        } catch (AccessDeniedException e) {
            System.out.println("Failed: " + e);
        }
    }

    private static void testUserNeedsACoursePassedForSubscription() {
        out.println("Testing if user can sign up with/without required courses.");
        Student zack = createDummyStudent();
        ArrayList<Course> req = new ArrayList<Course>();
        Course creq = createDummyCourse();
        req.add(creq);
        Course c = new Course(dummy_count++, "OOP with requirement", new GregorianCalendar(2000,
                Calendar.JANUARY, 1), new GregorianCalendar(2000,
                Calendar.JANUARY, 10), new GregorianCalendar(2000,
                Calendar.JANUARY, 25), req, 0);
        try {
            c.subscribeStudent(zack, new GregorianCalendar(2000, Calendar.JANUARY, 2));
            out.println("Failed: User could sign up without required course.");
        } catch (CourseException e) {
            out.println("Passed: " + e);
        }
        zack.addCompletedCourse(creq, new NormalGrade(2));
        try {
            c.subscribeStudent(zack, new GregorianCalendar(2000, Calendar.JANUARY, 2));
            out.println("Passed: User could sign up with required course.");
        } catch (CourseException e) {
            out.println("Failed: " + e);
        }
        out.println(hr);
        out.println("Getting Zacks grades (there should be 1 passed): ");
        Iterator<Course> it = zack.getCompletedCourses().keySet().iterator();
        out.println("CourseNr\tCourse\tpassed");
        while(it.hasNext()){
            Course key = it.next(); 
            out.println(key.getCourseNr() + "\t\t"+key.getTitle() + "\t" + zack.getCompletedCourses().get(key).hasPassed());
        }
    }

    private static void testCanSubscribeStudentToExams() throws CourseException {
        out.println("Trying to subscribe to a simple exam and oral exam...");
        Subscribeable exam = new Exam(1337, "Normal exam PI", new GregorianCalendar(2000, Calendar.JANUARY, 2),
            new GregorianCalendar(2000, Calendar.JANUARY, 5), new GregorianCalendar(2000, Calendar.JANUARY, 10),
            null, 0,  new GregorianCalendar(2000, Calendar.JANUARY, 10));
        Student zack = new Student(1234567, "Zack Brannigan");
        out.println(exam);
        out.println("At exam are " + exam.numberOfSubscribedStudents() + " students subscribed.. (before)");
        exam.subscribeStudent(zack, new GregorianCalendar(2000,
                Calendar.JANUARY, 4));
        out.println("At exam are " + exam.numberOfSubscribedStudents() + " students subscribed..(after)");
        exam = new OralExam(1337, "Oral exam PI2", new GregorianCalendar(2000, Calendar.JANUARY, 2),
            new GregorianCalendar(2000, Calendar.JANUARY, 5), new GregorianCalendar(2000, Calendar.JANUARY, 10),
            null, 0,  new GregorianCalendar(2000, Calendar.JANUARY, 10));
        out.println(exam);
        out.println("Again for oral one.. " + exam.numberOfSubscribedStudents() + " students subscribed.. (before)");
        exam.subscribeStudent(zack, new GregorianCalendar(2000,
                Calendar.JANUARY, 4));
        out.println("After:  " + exam.numberOfSubscribedStudents() + " students subscribed..");
    }
}
